package org.example.score;

import org.example.domain.CuttingPlanSolution;
import org.example.domain.CuttingSpot;
import org.example.domain.RectangularPiece;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.calculator.EasyScoreCalculator;

public class CuttingScoreCalculator implements EasyScoreCalculator<CuttingPlanSolution, HardSoftScore> {
    private static final int SAW_WIDTH = 5; // 切割锯宽度，单位与板材尺寸相同
    
    @Override
    public HardSoftScore calculateScore(CuttingPlanSolution solution) {
        int hardScore = 0;
        int softScore = 0;
        
        // 检查所有切割位置是否有重叠和切割间隙
        for (CuttingSpot spot1 : solution.getCuttingSpots()) {
            for (CuttingSpot spot2 : solution.getCuttingSpots()) {
                if (spot1 == spot2) continue;
                if (hasOverlapOrInsufficientGap(spot1, spot2)) {
                    hardScore -= 1000;
                }
            }
        }
        
        // 检查是否所有零件都在板料范围内
        for (CuttingSpot spot : solution.getCuttingSpots()) {
            if (!isWithinStock(spot)) {
                hardScore -= 1000;
            }
        }
        
        // 计算已使用的板料数量
        long usedStocks = solution.getCuttingSpots().stream()
            .map(CuttingSpot::getStock)
            .distinct()
            .count();
            
        softScore -= usedStocks * 1000; // 增加权重
        
        // 奖励相同零件的聚集
        softScore += calculatePieceGroupingScore(solution);
        
        // 奖励切割线对齐
        softScore += calculateCutLineAlignmentScore(solution);
        
        return HardSoftScore.of(hardScore, softScore);
    }
    
    private boolean hasOverlapOrInsufficientGap(CuttingSpot spot1, CuttingSpot spot2) {
        if (spot1.getStock() == null || spot2.getStock() == null || 
            !spot1.getStock().equals(spot2.getStock())) {
            return false;
        }
        
        int x1 = spot1.getX();
        int y1 = spot1.getY();
        int width1 = spot1.getPiece().getWidth();
        int height1 = spot1.getPiece().getHeight();
        
        int x2 = spot2.getX();
        int y2 = spot2.getY();
        int width2 = spot2.getPiece().getWidth();
        int height2 = spot2.getPiece().getHeight();
        
        // 检查垂直方向上是否有重叠
        boolean verticalOverlap = (y1 < y2 + height2) && (y2 < y1 + height1);
        // 检查水平方向上是否有重叠
        boolean horizontalOverlap = (x1 < x2 + width2) && (x2 < x1 + width1);
        
        if (verticalOverlap) {
            // 检查水平间距
            int gap = x1 < x2 ? x2 - (x1 + width1) : x1 - (x2 + width2);
            if (gap < SAW_WIDTH) {
                return true; // 间距不足
            }
        }
        
        if (horizontalOverlap) {
            // 检查垂直间距
            int gap = y1 < y2 ? y2 - (y1 + height1) : y1 - (y2 + height2);
            if (gap < SAW_WIDTH) {
                return true; // 间距不足
            }
        }
        
        // 检查对角线方向
        if (!verticalOverlap && !horizontalOverlap) {
            // 计算最近的角之间的距离
            double diagonalGap = calculateDiagonalGap(x1, y1, width1, height1, x2, y2, width2, height2);
            if (diagonalGap < SAW_WIDTH) {
                return true; // 对角线间距不足
            }
        }
        
        return false;
    }
    
    private double calculateDiagonalGap(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
        // 计算第一个矩形的四个角点
        int[][] corners1 = {
            {x1, y1},           // 左上
            {x1 + w1, y1},      // 右上
            {x1, y1 + h1},      // 左下
            {x1 + w1, y1 + h1}  // 右下
        };
        
        // 计算第二个矩形的四个角点
        int[][] corners2 = {
            {x2, y2},           // 左上
            {x2 + w2, y2},      // 右上
            {x2, y2 + h2},      // 左下
            {x2 + w2, y2 + h2}  // 右下
        };
        
        // 找到最小距离
        double minDistance = Double.MAX_VALUE;
        for (int[] p1 : corners1) {
            for (int[] p2 : corners2) {
                double distance = Math.sqrt(
                    Math.pow(p2[0] - p1[0], 2) + 
                    Math.pow(p2[1] - p1[1], 2)
                );
                minDistance = Math.min(minDistance, distance);
            }
        }
        
        return minDistance;
    }
    
    private boolean isWithinStock(CuttingSpot spot) {
        if (spot.getStock() == null || spot.getX() == null || spot.getY() == null) {
            return false;
        }
        
        return spot.getX() >= 0 &&
               spot.getY() >= 0 &&
               (spot.getX() + spot.getPiece().getWidth()) <= spot.getStock().getWidth() &&
               (spot.getY() + spot.getPiece().getHeight()) <= spot.getStock().getHeight();
    }
    
    // 计算相同零件聚集的分数
    private int calculatePieceGroupingScore(CuttingPlanSolution solution) {
        int groupingScore = 0;
        
        for (CuttingSpot spot1 : solution.getCuttingSpots()) {
            if (spot1.getStock() == null) continue;
            
            for (CuttingSpot spot2 : solution.getCuttingSpots()) {
                if (spot2.getStock() == null || spot1 == spot2) continue;
                
                // 如果是相同的零件且在同一块板料上
                if (spot1.getPiece().getId().equals(spot2.getPiece().getId()) 
                    && spot1.getStock().equals(spot2.getStock())) {
                    
                    // 计算两个零件之间的距离
                    double distance = calculateDistance(spot1, spot2);
                    
                    // 距离越近，得分越高
                    if (distance <= SAW_WIDTH) {
                        groupingScore += 50; // 相邻
                    } else if (distance <= SAW_WIDTH * 3) {
                        groupingScore += 20; // 较近
                    } else if (distance <= SAW_WIDTH * 5) {
                        groupingScore += 10; // 稍远
                    }
                }
            }
        }
        
        return groupingScore;
    }
    
    // 计算切割线对齐的分数
    private int calculateCutLineAlignmentScore(CuttingPlanSolution solution) {
        int alignmentScore = 0;
        
        for (CuttingSpot spot1 : solution.getCuttingSpots()) {
            if (spot1.getStock() == null) continue;
            
            for (CuttingSpot spot2 : solution.getCuttingSpots()) {
                if (spot2.getStock() == null || spot1 == spot2) continue;
                
                // 只考虑同一块板料上的零件
                if (spot1.getStock().equals(spot2.getStock())) {
                    // 检查水平对齐
                    if (Math.abs(spot1.getY() - spot2.getY()) <= 1) {
                        alignmentScore += 30;
                    }
                    if (Math.abs((spot1.getY() + spot1.getPiece().getHeight()) 
                        - (spot2.getY() + spot2.getPiece().getHeight())) <= 1) {
                        alignmentScore += 30;
                    }
                    
                    // 检查垂直对齐
                    if (Math.abs(spot1.getX() - spot2.getX()) <= 1) {
                        alignmentScore += 30;
                    }
                    if (Math.abs((spot1.getX() + spot1.getPiece().getWidth()) 
                        - (spot2.getX() + spot2.getPiece().getWidth())) <= 1) {
                        alignmentScore += 30;
                    }
                }
            }
        }
        
        return alignmentScore;
    }
    
    // 计算两个切割位置之间的距离
    private double calculateDistance(CuttingSpot spot1, CuttingSpot spot2) {
        // 计算两个矩形的中心点
        double x1 = spot1.getX() + spot1.getPiece().getWidth() / 2.0;
        double y1 = spot1.getY() + spot1.getPiece().getHeight() / 2.0;
        double x2 = spot2.getX() + spot2.getPiece().getWidth() / 2.0;
        double y2 = spot2.getY() + spot2.getPiece().getHeight() / 2.0;
        
        // 返回欧几里得距离
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    }
} 